#ifndef __TERMINAL_H
#define __TERMINAL_H
#include "matrix.h"
#include "console.h"
#include "oarray.h"
#include "symbol.h"
#include "overload.h"

/* the symbol cluster of terminal */
#define MX_TERMINAL_SYMBOL_CLUSTER terminal

/* terminal exit code */
#define MX_TERMINAL_EXECRET_OK 0
#define MX_TERMINAL_EXECRET_ERR 1               // unknown error
#define MX_TERMINAL_EXECRET_ERR_USAGE 2         // command usage error
#define MX_TERMINAL_EXECRET_ERR_SYNTAX 3        // shell command syntax error
#define MX_TERMINAL_EXECRET_NOVAR 126           // no memory space for variable
#define MX_TERMINAL_EXECRET_CMDNOTFOUND 127     // command not found
#define MX_TERMINAL_EXECRET_ILLEGAL 128         // illegal return value
#define MX_TERMINAL_EXECRET_BREAK 130           // break by user

/**
 * register a cmd without the descriptor
 * @prototype: void mx_terminal_register (mx_string_pt name, mx_terminal_callback_t func);
 * @prototype: void mx_terminal_register (mx_string_pt name, mx_string_pt end, mx_terminal_callback_t func);
 * @note: it's a package for `mx_terminal_cmdadd`
 */
#define mx_terminal_register(...) OVERLOAD(__mx_terminal_register, __VA_ARGS__)
#define __mx_terminal_register1(name, func, p2, p3, p4, p5, p6, p7) {   \
    static mx_terminal_cmd_t _i_tcmd;    \
    mx_terminal_cmdadd(&_i_tcmd, name, mx_null, func);  \
}
#define __mx_terminal_register2(name, end, func, p3, p4, p5, p6, p7) {   \
    static mx_terminal_cmd_t _i_tcmd;    \
    mx_terminal_cmdadd(&_i_tcmd, name, end, func);  \
}

/**
 * export a cmd
 * @prototype: MX_TERMINAL_EXPORT(cmd, func)
 * @prototype: MX_TERMINAL_EXPORT(cmd, end, func)
 */
#define MX_TERMINAL_EXPORT(...) OVERLOAD(__MX_TERMINAL_EXPORT, __VA_ARGS__)
#define __MX_TERMINAL_EXPORT1(cmdname, cmdfunc, p2, p3, p4, p5, p6, p7)  \
    static mx_terminal_cmd_t var##_##cmdfunc = {  \
        .name = cmdname,    \
        .end = mx_null,     \
        .func = cmdfunc     \
    };  \
    SYMBOL_OBJECT_EXPORT(MX_TERMINAL_SYMBOL_CLUSTER, var##_##cmdfunc, 1);
#define __MX_TERMINAL_EXPORT2(cmdname, cmdend, cmdfunc, p3, p4, p5, p6, p7)  \
    static mx_terminal_cmd_t var##_##cmdfunc = {  \
        .name = cmdname,    \
        .end = cmdend,      \
        .func = cmdfunc     \
    };  \
    SYMBOL_OBJECT_EXPORT(MX_TERMINAL_SYMBOL_CLUSTER, var##_##cmdfunc, 1);

/* terminal command callback */
typedef mx_size_t (*mx_terminal_callback_t) (mx_usize_t argc, char** argv);

/* terminal code operation callback */
typedef void (*mx_terminal_code_handler_t) (mx_uint8_t ch);
typedef void (*mx_terminal_csi_handler_t) (mx_uint8_pt pch, mx_uint8_t len);

/* the descriptor of terminal cmd definition */
typedef struct
{
    mx_string_pt name;          // the command's name, e.g. if
    mx_string_pt end;           // the command's terminator, e.g. fi, = null: the `;` or `\0` is terminator
    mx_terminal_callback_t func;
    mx_oarray_node_t onode;
} mx_terminal_cmd_t, *mx_terminal_cmd_pt;

typedef struct
{
    mx_terminal_code_handler_t ptable_code_handler;
    mx_terminal_code_handler_t ctrl_code_handler;
    mx_terminal_csi_handler_t csi_code_handler;
} mx_terminal_ops_t, *mx_terminal_ops_pt;

/* add a command to list */
void mx_terminal_cmdadd (mx_terminal_cmd_pt pcmd, mx_string_pt name, mx_string_pt end,
                         mx_terminal_callback_t func);
/* remove a command from list */
void mx_terminal_cmdremove (mx_terminal_cmd_pt pcmd);
/* let the console silent ot not */
void mx_terminal_setsilent (mx_bool_t issilent);
/* write a string on terminal */
void mx_terminal_write (const mx_string_pt fmt, ...);
/* write a string on terminal with newline */
void mx_terminal_writel (const mx_string_pt fmt, ...);
/* run a command, multiple commands use `;` to separate */
mx_size_t mx_terminal_run (mx_string_pt str);
/* run a command, and not modify the input string, multiple commands use `;` to separate */
mx_size_t mx_terminal_runnm (mx_string_pt str);
/* read input from terminal */
mx_int16_t mx_terminal_read (mx_usize_t tick);

#endif
